// i.r(t), i.d(t, { default: () => RenderToTexture })
import Module from "./Module"
import SymbolList from "../SymbolList"
import RenderLayers from "../utils/RenderLayers"
import * as THREE from "three"
import a from "../shader/31090"
import l from "../shader/69383"
import funcUtil from "../utils/func.util"

import ae from "../shader/74624"
import be from "../shader/57223"
import ce from "../shader/64799"
import de from "../shader/72488"
import ee from "../shader/47151"
import fe from "../shader/14499"
import ge from "../shader/95611"
import he from "../shader/54779"

const h = {
  circle_projection: new THREE.RawShaderMaterial({
    uniforms: {
      textureSampleScale: { value: 5 },
      panoTexture: { value: null },
      borderSize: { value: 0 },
      borderColor: { value: new THREE.Vector4(1, 1, 1, 1) }
    },
    depthWrite: !1,
    depthTest: !0,
    side: THREE.DoubleSide,
    vertexShader: funcUtil.addNR(l, ae),
    fragmentShader: funcUtil.addNR(a, be)
  }),
  equirectangular: new THREE.RawShaderMaterial({
    uniforms: { cubemap: { value: null }, yaw: { value: 0 } },
    depthWrite: !1,
    depthTest: !1,
    vertexShader: funcUtil.addNR(l, ce),
    fragmentShader: funcUtil.addNR(a, de)
  }),
  compose: new THREE.RawShaderMaterial({
    uniforms: { mask: { value: null }, bg: { value: null } },
    transparent: !0,
    vertexShader: funcUtil.addNR(l, ee),
    fragmentShader: funcUtil.addNR(a, fe)
  }),
  copyOpaque: new THREE.RawShaderMaterial({
    uniforms: { texture: { value: null } },
    vertexShader: funcUtil.addNR(l, ge),
    fragmentShader: funcUtil.addNR(a, he)
  })
}
import {
  RenderToCubemapCommand,
  RequestRenderTargetCommand,
  Context2dCaptureCommand,
  ScreenCaptureCommand,
  EquirectangularCommand,
  ComposeTargetsCommand
} from "../command/render.command"
class RenderTarget {
  _renderer: any
  _renderTarget: any
  constructor(e, t) {
    ;(this._renderer = e), (this._renderTarget = t)
  }
  get target() {
    return this._renderTarget
  }
  get width() {
    return this._renderTarget.width
  }
  get height() {
    return this._renderTarget.height
  }
  get bpp() {
    return 4
  }
  readRenderTargetData(e) {
    const t = this._renderTarget.width,
      i = this._renderTarget.height
    return (e = e || new Uint8Array(t * i * 4)), this._renderer.readRenderTargetPixels(this._renderTarget, 0, 0, t, i, e), e
  }
  setSize(e, t) {
    this._renderTarget.setSize(e, t)
  }
  dispose() {
    this._renderTarget.dispose()
  }
}
import Logger from "../utils/Logger"
import TypedMesh from "../mesh/TypedMesh"
import { phaseState } from "../enum/phase.enum"
const g = new Logger("rtt")
export default class RenderToTexture extends Module {
  circleProjectionPlane: TypedMesh
  equirectProjectionPlane: TypedMesh
  composePlane: TypedMesh
  copyPlane: TypedMesh
  cachedSize: THREE.Vector2
  cachedViewport: THREE.Vector4
  cachedViewport2: THREE.Vector4
  solidWhiteTexture: THREE.DataTexture
  debugRenderOffset: number
  engine: any
  cwfRenderer: any
  renderer: any
  camera: THREE.Camera
  orthoCamera: THREE.OrthographicCamera
  scene: THREE.Scene
  constructor() {
    super(...arguments),
      (this.name = "render-to-texture"),
      (this.circleProjectionPlane = new TypedMesh(new THREE.PlaneBufferGeometry(1, 1), h.circle_projection)),
      (this.equirectProjectionPlane = new TypedMesh(new THREE.PlaneBufferGeometry(1, 1), h.equirectangular)),
      (this.composePlane = new TypedMesh(new THREE.PlaneBufferGeometry(1, 1), h.compose)),
      (this.copyPlane = new TypedMesh(new THREE.PlaneBufferGeometry(1, 1), h.copyOpaque)),
      (this.cachedSize = new THREE.Vector2()),
      (this.cachedViewport = new THREE.Vector4()),
      (this.cachedViewport2 = new THREE.Vector4()),
      (this.solidWhiteTexture = new THREE.DataTexture(new Uint8Array([255, 255, 255, 255]), 1, 1)),
      (this.debugRenderOffset = 0)
  }
  async init(e, t) {
    this.engine = t
    const i = await t.getModuleBySymbol(SymbolList.WEBGL_RENDERER)
    ;(this.cwfRenderer = i.cwfRenderer),
      (this.renderer = i.cwfRenderer.renderer),
      (this.camera = new THREE.Camera()),
      (this.orthoCamera = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5, 0, 1)),
      (this.camera.layers.mask = RenderLayers.ALL.mask),
      (this.scene = new THREE.Scene()),
      (this.scene.name = "rtt"),
      (this.scene.autoUpdate = !1),
      this.bindings.push(t.commandBinder.addBinding(RequestRenderTargetCommand, async e => new RenderTarget(this.renderer, this.createRenderTarget2D(0)))),
      this.bindings.push(
        t.commandBinder.addBinding(Context2dCaptureCommand, async e => {
          this.renderContext(e.renderTarget.target, e.context)
        })
      ),
      this.bindings.push(
        t.commandBinder.addBinding(ScreenCaptureCommand, async e => {
          this.render(e.renderTarget.target, e.sceneObject, e.camera)
        })
      ),
      this.bindings.push(
        t.commandBinder.addBinding(EquirectangularCommand, async e => {
          this.renderEquirectangular(e.texture, e.renderTarget.target, e.heading)
        })
      ),
      this.bindings.push(
        t.commandBinder.addBinding(RenderToCubemapCommand, async e => {
          this.render(e.renderTarget, e.sceneObject, e.camera)
        })
      ),
      this.bindings.push(
        t.commandBinder.addBinding(ComposeTargetsCommand, async e => {
          this.compose(e.target1.target, e.target2.target, e.target2.target)
        })
      )
  }
  getRenderSize() {
    const e = this.renderer.getPixelRatio(),
      t = this.renderer.getSize(this.cachedSize)
    return (t.width *= e), (t.height *= e), t
  }
  onUpdate() {
    this.debugRenderOffset = 0
  }
  createRenderTarget2D(e, t = e, i?, n = !0) {
    const s = new THREE.WebGLRenderTarget(e, t, i)
    return (s.texture.generateMipmaps = n), s
  }
  clearRenderTarget2D(e) {
    this.renderer.setRenderTarget(e), this.renderer.clear(), this.renderer.setRenderTarget(null)
  }
  disposeRenderTarget2D(e: THREE.WebGLRenderTarget) {
    e.texture.dispose()
    e.depthTexture && e.depthTexture.dispose()
    e.dispose()
  }
  getRenderTargetData(e, t) {
    const i = e.width,
      n = e.height
    return (t = t || new Uint8Array(i * n * 4)), this.renderer.readRenderTargetPixels(e, 0, 0, i, n, t), t
  }
  compose(e, t, i = this.solidWhiteTexture) {
    ;(this.composePlane.material["uniforms"].bg.value = this.isRenderTarget(t) ? t.texture : t),
      (this.composePlane.material["uniforms"].mask.value = this.isRenderTarget(i) ? i["texture"] : i),
      this.scene.add(this.composePlane),
      this.overrideRenderTarget(e, () => {
        this.renderer.render(this.scene, this.orthoCamera)
      }),
      this.scene.remove(this.composePlane)
  }
  copyTextureOpaque(e, t) {
    ;(this.copyPlane.material["uniforms"].texture.value = t && this.isRenderTarget(t) ? t.texture : t),
      this.scene.add(this.copyPlane),
      this.overrideRenderTarget(e, () => {
        this.renderer.render(this.scene, this.orthoCamera)
      }),
      this.scene.remove(this.copyPlane)
  }
  copyTexture(e, t) {
    return t.format === THREE.RGBFormat ? this.copyTextureOpaque(e, t) : this.compose(e, t)
  }
  resizeTexture(e, t) {
    const i = new THREE.WebGLRenderTarget(t, t)
    return (i.texture.format = e.format), i.texture.addEventListener("dispose", i.dispose.bind(i)), this.copyTexture(i, e), i.texture
  }
  renderToScreen(e, t = !0, i, n) {
    ;(t ? Promise.resolve() : this.engine.after(phaseState.Render)).then(() => {
      const t = this.isRenderTarget(e) ? e.width : e.image.width,
        s = this.isRenderTarget(e) ? e.height : e.image.height,
        r = i ? i.x : this.debugRenderOffset,
        o = i ? i.y : 60
      this.renderer.getViewport(this.cachedViewport),
        this.renderer.setViewport(r, o, t, s),
        this.compose(null, e, n),
        this.renderer.setViewport(this.cachedViewport),
        i || (this.debugRenderOffset += t + 4)
    })
  }
  isRenderTarget(e) {
    return e.hasOwnProperty("texture")
  }
  renderContext(e, t) {
    ;(e.texture.image = t.canvas), (e.texture.needsUpdate = !0)
  }
  render(e, t, i, n?) {
    const s = t.parent
    if ((s && this.scene.applyMatrix4(s.matrixWorld), this.scene.add(t), e.isWebGLCubeRenderTarget)) {
      const t = new THREE.CubeCamera(0.01, 1e3, e)
      i.getWorldPosition(t.position),
        i.getWorldQuaternion(t.quaternion),
        this.scene.add(t),
        this.scene.updateMatrixWorld(),
        (t.layers.mask = n ? n.mask : i.layers.mask),
        t.update(this.renderer, this.scene),
        this.scene.remove(t)
    } else {
      i.getWorldPosition(this.camera.position),
        i.getWorldQuaternion(this.camera.quaternion),
        this.camera.projectionMatrix.copy(i.projectionMatrix),
        (this.camera.layers.mask = n ? n.mask : i.layers.mask)
      const t = this.renderer.getSize(this.cachedSize),
        s = t.width / t.height,
        r = e.width / e.height,
        o = this.camera.projectionMatrix
      s > r ? (o.elements[0] = o.elements[5] / r) : (o.elements[5] = o.elements[0] * r),
        this.overrideRenderTarget(e, () => {
          this.renderer.clear(), this.renderer.render(this.scene, this.camera)
        })
    }
    return s && (s.add(t), this.scene.matrixWorld.identity()), this.scene.remove(t), e
  }
  setScissors(e, t) {
    if (e) {
      if (!t) throw Error("Rect to restrict rendering to required when enabling scissors.")
      this.renderer.setScissorTest(!0), this.renderer.setScissor(t.x, t.y, t.width, t.height)
    } else {
      const e = this.renderer.getSize(this.cachedSize)
      this.renderer.setScissor(0, 0, e.width, e.height), this.renderer.setScissorTest(!1)
    }
  }
  renderSphericalProjection(e, t, i, n, s) {
    const r = h.circle_projection
    r.uniforms.borderColor.value.copy(s || new THREE.Vector4(1, 1, 1, 1)),
      (r.uniforms.borderSize.value = n || 0),
      (r.uniforms.textureSampleScale.value = i || 5),
      (r.uniforms.panoTexture.value = e),
      this.scene.add(this.circleProjectionPlane),
      (this.circleProjectionPlane.position.z = 0),
      this.overrideRenderTarget(t, () => {
        this.renderer.render(this.scene, this.orthoCamera)
      }),
      this.scene.remove(this.circleProjectionPlane)
  }
  renderEquirectangular(e, t, i = 0) {
    const n = this.equirectProjectionPlane.material
    ;(n["uniforms"].cubemap.value = e),
      (n["uniforms"].yaw.value = i),
      this.scene.add(this.equirectProjectionPlane),
      (this.equirectProjectionPlane.position.z = 0),
      this.overrideRenderTarget(t, () => {
        this.renderer.render(this.scene, this.orthoCamera)
      }),
      this.scene.remove(this.equirectProjectionPlane),
      (n["uniforms"].cubemap.value = null),
      (n["uniforms"].yaw.value = 0)
  }
  overrideRenderTarget(e, t) {
    const i = this.renderTargetSwap(e)
    t(), this.renderTargetRestore(i)
  }
  renderTargetSwap(e) {
    const t = this.renderer.xr.enabled,
      i = this.renderer.getRenderTarget(),
      n = this.renderer.getViewport(this.cachedViewport2)
    return (this.renderer.xr.enabled = !1), this.renderer.setRenderTarget(e), { xr: t, rtt: i, viewport: n }
  }
  renderTargetRestore(e) {
    ;(this.renderer.xr.enabled = e.xr), this.renderer.setRenderTarget(e.rtt), this.renderer.setViewport(e.viewport)
  }
  renderAndReadAsync(e, t, i) {
    const n = i.buffer
    if (!this.renderer.capabilities.isWebGL2)
      return (
        g.debug("renderAsync call webgl2, falling back to sync render/read"),
        this.render(e.target, e.mesh, e.camera),
        Promise.resolve(this.getRenderTargetData(e.target, n))
      )
    const s = this.renderTargetSwap(e.target),
      r = this.renderer.getContext(),
      o = i.webglBuffer || r.createBuffer()
    if (!o) throw Error("Unable to create pack buffer")
    return (
      r.bindBuffer(r.PIXEL_PACK_BUFFER, o),
      r.bufferData(r.PIXEL_PACK_BUFFER, n.byteLength, r.DYNAMIC_READ),
      e.clear && this.renderer.clear(),
      this.renderer.render(e.mesh, e.camera),
      r.readPixels(t.x, t.y, t.width, t.height, r.RGBA, r.UNSIGNED_BYTE, 0),
      r.bindBuffer(r.PIXEL_PACK_BUFFER, null),
      this.renderTargetRestore(s),
      this.cwfRenderer
        .fence(r)
        .then(
          () => (
            r.bindBuffer(r.PIXEL_PACK_BUFFER, o),
            r.getBufferSubData(r.PIXEL_PACK_BUFFER, 0, n),
            r.bindBuffer(r.PIXEL_PACK_BUFFER, null),
            i.webglBuffer || r.deleteBuffer(o),
            n
          )
        )
    )
  }
}
